import java.io.*;

public class Tokens {

	private BufferedReader br;
	private int tokenVal;
	private String tokenString;
	private String currentLine;
	private final String[] keywordArray;

	public Tokens(String fileName) {
		try {
			this.br = new BufferedReader(new FileReader(fileName));
			currentLine = "";
		} catch (FileNotFoundException e) {
			System.out.println("File not Found: " + fileName);
		}
		this.keywordArray = new String[31];
		this.populateKeyword();
	}

	private void populateKeyword() {
		keywordArray[1] = "program";
		keywordArray[2] = "begin";
		keywordArray[3] = "end";
		keywordArray[4] = "int";
		keywordArray[5] = "if";
		keywordArray[6] = "then";
		keywordArray[7] = "else";
		keywordArray[8] = "while";
		keywordArray[9] = "loop";
		keywordArray[10] = "read";
		keywordArray[11] = "write";
		keywordArray[12] = ";";
		keywordArray[13] = ",";
		keywordArray[14] = "=";
		keywordArray[15] = "!";
		keywordArray[16] = "[";
		keywordArray[17] = "]";
		keywordArray[18] = "&&";
		keywordArray[19] = "||";
		keywordArray[20] = "(";
		keywordArray[21] = ")";
		keywordArray[22] = "+";
		keywordArray[23] = "-";
		keywordArray[24] = "*";
		keywordArray[25] = "!=";
		keywordArray[26] = "==";
		keywordArray[27] = "<";
		keywordArray[28] = ">";
		keywordArray[29] = "<=";
		keywordArray[30] = ">=";
	}

	protected void skipToken() {
		generateRawToken();
		while (this.tokenVal == 0) {
			generateRawToken();
		}
	}

	protected String idName() {
		return tokenString;
	}

	public void generateRawToken() {
		
		try {
			if (currentLine.isEmpty()) {
				if (br.ready()) {
					currentLine = br.readLine();
				} else {
					this.tokenVal = 33;
					this.tokenString = "EOF";
				}
			}
		} catch (IOException e) {
			System.err.println("File unreadable!");
			System.exit(0);
		}

		StringBuilder nextToken = new StringBuilder();
		char nextChar = ' ';

		StringBuilder tokenLine = new StringBuilder(currentLine.trim());
		tokenLine = this.removeWhitespace(tokenLine);

		// If the current line is not empty, retrieve the first character.
		if (tokenLine.length() > 0) {
			nextChar = tokenLine.charAt(0);
			tokenLine.deleteCharAt(0);
			nextToken.append(nextChar);
		} else { 
			this.tokenVal = 0;
			this.tokenString = "";
			try {
				if (!br.ready()) {
					this.tokenVal = 33;
					this.tokenString = "EOF";
					br.close();
				}
			} catch (IOException e) {
				System.err.println("File unreadable!");
				System.exit(0);
			}
		}

		if (Character.isUpperCase(nextChar)) {
			if (tokenLine.length() > 0) {
				nextChar = tokenLine.charAt(0);
				while ((Character.isUpperCase(nextChar) || Character
						.isDigit(nextChar))) {
					nextToken.append(nextChar);
					tokenLine.deleteCharAt(0);
					nextChar = tokenLine.charAt(0);
				}
			}
			this.tokenVal = 32;
			this.tokenString = nextToken.toString();
			this.currentLine = tokenLine.toString();
		} else if (Character.isDigit(nextChar)) { // If the next character is an
													// integer.
			if (tokenLine.length() > 0) {
				nextChar = tokenLine.charAt(0);
				while (Character.isDigit(nextChar)) {
					nextToken.append(nextChar);
					tokenLine.deleteCharAt(0);
					nextChar = tokenLine.charAt(0);
				}
			}
			this.tokenVal = 31;
			this.tokenString = nextToken.toString();
			this.currentLine = tokenLine.toString();
		} else {// current token is a keyword.
			// Designate token values according to keywordList array positions.
			if (nextChar == ';') {
				this.tokenVal = 12;
			} else if (nextChar == ',') {
				this.tokenVal = 13;
			} else if (nextChar == '=') {
				if (tokenLine.charAt(0) != '=') {
					this.tokenVal = 14;
				} else { // if the token is "==")
					this.tokenVal = 26;
					nextToken.append(nextToken.charAt(0));
					tokenLine.deleteCharAt(0);
				}
			} else if (nextChar == '!') {
				if (tokenLine.charAt(0) != '=') {
					this.tokenVal = 15;
				} else {
					this.tokenVal = 25;
					nextToken.append(nextToken.charAt(0));
					tokenLine.deleteCharAt(0);
				}
			} else if (nextChar == '[') {
				this.tokenVal = 16;
			} else if (nextChar == ']') {
				this.tokenVal = 17;
			} else if (nextChar == '&') {
				if (tokenLine.charAt(0) == '&') {
					this.tokenVal = 18;
					nextToken.append(tokenLine.charAt(0));
					tokenLine.deleteCharAt(0);
				}
			} else if (nextChar == '|') {
				if (tokenLine.charAt(0) == '|') {
					this.tokenVal = 19;
					nextToken.append(tokenLine.charAt(0));
					tokenLine.deleteCharAt(0);
				}
			} else if (nextChar == '(') {
				this.tokenVal = 20;
			} else if (nextChar == ')') {
				this.tokenVal = 21;
			} else if (nextChar == '+') {
				this.tokenVal = 22;
			} else if (nextChar == '-') {
				this.tokenVal = 23;
			} else if (nextChar == '*') {
				this.tokenVal = 24;
			} else if (nextChar == '<') {
				if (tokenLine.charAt(0) != '=') {
					this.tokenVal = 27;
				} else { // if the token is "<=")
					this.tokenVal = 29;
					nextToken.append(nextToken.charAt(0));
					tokenLine.deleteCharAt(0);
				}
			} else if (nextChar == '>') {
				if (tokenLine.charAt(0) != '=') {
					this.tokenVal = 28;
				} else { // if the token is ">=")
					this.tokenVal = 30;
					nextToken.append(nextToken.charAt(0));
					tokenLine.deleteCharAt(0);
				}
			} else if (nextChar == 'p') {
				if (tokenLine.substring(0, 6).compareTo("rogram") == 0) {
					this.tokenVal = 1;
					nextToken.append(tokenLine.substring(0, 6));
					tokenLine.delete(0, 6);
				}
			} else if (nextChar == 'b') {
				if (tokenLine.substring(0, 4).compareTo("egin") == 0) {
					this.tokenVal = 2;
					nextToken.append(tokenLine.substring(0, 4));
					tokenLine.delete(0, 4);
				}
			} else if (nextChar == 'e') {
				if (tokenLine.substring(0, 2).compareTo("nd") == 0) {
					this.tokenVal = 3;
					nextToken.append(tokenLine.substring(0, 2));
					tokenLine.delete(0, 2);
				} else if (tokenLine.substring(0, 3).compareTo("lse") == 0) {
					this.tokenVal = 7;
					nextToken.append(tokenLine.substring(0, 3));
					tokenLine.delete(0, 3);
				}
			} else if (nextChar == 'i') {
				if (tokenLine.substring(0, 2).compareTo("nt") == 0) {
					this.tokenVal = 4;
					nextToken.append(tokenLine.substring(0, 2));
					tokenLine.delete(0, 2);
				} else if (tokenLine.charAt(0) == 'f') {
					this.tokenVal = 5;
					nextToken.append(tokenLine.charAt(0));
					tokenLine.deleteCharAt(0);
				}
			} else if (nextChar == 't') {
				if (tokenLine.substring(0, 3).compareTo("hen") == 0) {
					this.tokenVal = 6;
					nextToken.append(tokenLine.substring(0, 3));
					tokenLine.delete(0, 3);
				}
			} else if (nextChar == 'w') {
				if (tokenLine.substring(0, 4).compareTo("hile") == 0) {
					this.tokenVal = 8;
					nextToken.append(tokenLine.substring(0, 4));
					tokenLine.delete(0, 4);
				} else if (tokenLine.substring(0, 4).compareTo("rite") == 0) {
					this.tokenVal = 11;
					nextToken.append(tokenLine.substring(0, 4));
					tokenLine.delete(0, 4);
				}
			} else if (nextChar == 'l') {
				if (tokenLine.substring(0, 3).compareTo("oop") == 0) {
					this.tokenVal = 9;
					nextToken.append(tokenLine.substring(0, 3));
					tokenLine.delete(0, 3);
				}
			} else if (nextChar == 'r') {
				if (tokenLine.substring(0, 3).compareTo("ead") == 0) {
					this.tokenVal = 10;
					nextToken.append(tokenLine.substring(0, 3));
					tokenLine.delete(0, 3);
				}
			} else if (nextChar != ' ') {
				System.err.println("Invalid instruction format: " + nextChar);
				System.exit(0);
			}
			this.currentLine = tokenLine.toString();
			if (this.tokenVal != 33) {
				this.tokenString = this.keywordArray[this.tokenVal];
			}
		}
	}


	protected int getToken() {
		return tokenVal;
	}

	public StringBuilder removeWhitespace(StringBuilder input) {
		StringBuilder inputLine = input;
		if (inputLine.length() > 0) {
			char nextChar = inputLine.charAt(0);
			while (Character.isWhitespace(nextChar)) {
				inputLine.deleteCharAt(0);
				nextChar = inputLine.charAt(0);
			}
		}
		return inputLine;
	}

	public String getCurrentLine() {
		return this.currentLine;
	}
}